<template>
  <div class="chat-stream">
    <div class="output" ref="outputRef">
      <div v-if="messages.length === 0" class="placeholder">开始提问吧...</div>
      <div v-else v-for="(msg, index) in messages" :key="index" class="message">
        {{ msg }}
      </div>
    </div>
    <div class="input-area">
      <input
          v-model="question"
          placeholder="请输入你的问题"
          class="input"
          @keydown.enter="send"
          :disabled="loading"
      />
      <button @click="send" class="button" :disabled="loading">
        {{ loading ? '发送中...' : '发送' }}
      </button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'ChatStream',
  data() {
    return {
      question: '',
      messages: [],
      loading: false,
      outputRef: null,
      buffer: '',
      currentReply: '',
    }
  },
  mounted() {
    this.outputRef = this.$refs.outputRef
  },
  methods: {
    scrollToBottom() {
      this.$nextTick(() => {
        if (this.outputRef) {
          this.outputRef.scrollTop = this.outputRef.scrollHeight
        }
      })
    },
    send() {
      if (!this.question.trim() || this.loading) return

      const current = this.question
      this.messages.push('我：' + current)
      this.question = ''
      this.loading = true
      this.buffer = ''
      this.currentReply = ''
      this.messages.push('AI：')

      fetch('/ai/openAiMemory/streamBak?message=' + JSON.stringify(current)).then(response => {
        const reader = response.body.getReader()
        const decoder = new TextDecoder()
        let leftover = new Uint8Array(0)

        const concatUint8Arrays = (a, b) => {
          const c = new Uint8Array(a.length + b.length)
          c.set(a, 0)
          c.set(b, a.length)
          return c
        }

        const read = () => {
          reader.read().then(({ done, value }) => {
            if (done) {
              if (leftover.length > 0) {
                const text = decoder.decode(leftover, { stream: false })
                this.updateMessage(text)
              }
              this.loading = false
              this.scrollToBottom()
              return
            }

            let chunk = concatUint8Arrays(leftover, value)

            let validLength = chunk.length
            while (validLength > 0) {
              try {
                decoder.decode(chunk.subarray(0, validLength), { stream: true })
                break
              } catch {
                validLength--
              }
            }

            if (validLength === 0) {
              leftover = chunk
              read()
              return
            }

            const decoded = decoder.decode(chunk.subarray(0, validLength), { stream: true })
            leftover = chunk.subarray(validLength)

            this.processSSE(decoded)
            read()
          }).catch(err => {
            this.loading = false
            this.messages[this.messages.length - 1] = 'AI：请求出错，请稍后重试'
            console.error(err)
          })
        }

        this.processSSE = (text) => {
          const lines = text.split('\n')
          for (const line of lines) {
            if (!line.startsWith('data:')) continue
            const content = line.replace(/^data:\s*/, '').trim()

            if (content === '[DONE]') {
              if (this.buffer) {
                this.updateMessage(this.buffer)
                this.buffer = ''
              }
              this.loading = false
              this.scrollToBottom()
              return
            }

            this.buffer += content
            this.updateMessage(this.buffer)
          }
        }

        this.updateMessage = (text) => {
          this.currentReply = text
          this.messages.splice(this.messages.length - 1, 1, 'AI：' + this.currentReply)
          this.scrollToBottom()
        }

        read()
      })
    }
  }
}
</script>

<style scoped>
.chat-stream {
  padding: 20px;
  max-width: 80%;
  max-height: 80%;
  margin: auto;
}

.input-area {
  display: flex;
  margin-top: 10px;
}

.input {
  flex: 1;
  padding: 6px;
  margin-right: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
}

.button {
  padding: 6px 12px;
  border-radius: 4px;
  background-color: #409EFF;
  color: white;
  border: none;
  cursor: pointer;
}

.button:disabled {
  background-color: #a0cfff;
  cursor: not-allowed;
}

.output {
  white-space: pre-wrap;
  word-wrap: break-word;
  overflow-wrap: break-word;
  border: 1px solid #ccc;
  padding: 10px;
  height: 300px;
  overflow-y: auto;
  background-color: #f8f8f8;
}

.message {
  margin-bottom: 10px;
}

.placeholder {
  color: #999;
  font-style: italic;
}
</style>
